<head><meta name="viewport" content="user-scalable=no,initial-scale=1,maximum-scale=1">
<meta charset=utf-8>
<style>
body { margin: 0px; }
canvas { width:100%; height:100%; overflow: hidden; }
</style>
<script type="text/javascript" src="../h3du_min.js"></script>
<script type="text/javascript" src="../extras/frame.js"></script>
<script type="text/javascript" src="../extras/curvetube.js"></script>
<script type="text/javascript" src="../extras/evaluators.js"></script>
<script type="text/javascript" src="../extras/path.js"></script>
<script type="text/javascript" src="demoutil.js"></script>
<script type="text/javascript" src="../extras/meshjson.js"></script>
</head>
<body>
<canvas id=canvas></canvas>
<div style="position:absolute;left:0;top:0;width:25%">Involute, evolute, and inverse curve
of an ellipse</div>
<script id="demo">
/* global H3DU */
// <!--
/*
 Any copyright to this file is released to the Public Domain.
 http://creativecommons.org/publicdomain/zero/1.0/
 If you like this, you should donate
 to Peter O. (original author of
 the Public Domain HTML 3D Library) at:
 http://peteroupc.github.io/
*/

function makeCurvePath(curve) {
  "use strict";
  return new H3DU.CurveBuilder().position(curve).evalCurve(H3DU.Mesh.LINES, 60).toMeshBuffer()
}

function _vecNormInPlaceAndScale(vec, scale) {
  "use strict";
  var len = 0;
  for(var i = 0; i < vec.length; i++) {
    len += vec[i] * vec[i];
  }
  len = Math.sqrt(len);
  if(len !== 0) {
    var newscale = 1.0 / len * scale;
    for(i = 0; i < vec.length; i++) {
      vec[i] *= newscale;
    }
  } else {
    for(i = 0; i < vec.length; i++) {
      vec[i] *= scale;
    }
  }
  return vec;
}
function _vecAdd(v1, v2) {
  "use strict";
  var ret = [];
  for(var i = 0; i < v1.length; i++) {
    ret[i] = v1[i] + v2[i];
  }
  return ret;
}
function _vecScale(v1, s) {
  "use strict";
  var ret = [];
  for(var i = 0; i < v1.length; i++) {
    ret[i] = v1[i] * s;
  }
  return ret;
}

function curveInvolute(evaluator) {
  "use strict";
  var neweval = evaluator;
  return {
    "evaluate":function(u) {
      var arclen = neweval.arcLength(u);
      var velocity = neweval.velocity(u);
      var position = neweval.evaluate(u);
      return _vecAdd(position,
        _vecNormInPlaceAndScale(velocity, -arclen));
    },
    "endPoints":function() {
      return neweval.endPoints();
    }
  };
}
function curveEvolute(evaluator) {
  "use strict";
  var neweval = evaluator;
  return {
    "evaluate":function(u) {
      var position = neweval.evaluate(u);
      var velocity = neweval.velocity(u);
      var accel = neweval.accel(u);
      var denom = velocity[0] * accel[1] - accel[0] * velocity[1];
      var numpart = velocity[0] * velocity[0] + velocity[1] * velocity[1];
      return [
        position[0] - numpart * velocity[1] / denom,
        position[1] + numpart * velocity[0] / denom,
        0
      ];
    },
    "endPoints":function() {
      return neweval.endPoints();
    }
  };
}
/* exported curveRadialCurve */
function curveRadialCurve(evaluator, ox, oy) {
  "use strict";
  var neweval = evaluator;
  return {
    "evaluate":function(u) {
      var velocity = neweval.velocity(u);
      var accel = neweval.accel(u);
      var denom = velocity[0] * accel[1] - accel[0] * velocity[1];
      var numpart = velocity[0] * velocity[0] + velocity[1] * velocity[1];
      return [
        ox - numpart * velocity[1] / denom,
        oy + numpart * velocity[0] / denom,
        0
      ];
    },
    "endPoints":function() {
      return neweval.endPoints();
    }
  };
}
function curveOrthotomic(evaluator, ox, oy) {
  "use strict";
  var neweval = evaluator;
  return {
    "evaluate":function(u) {
      var position = neweval.evaluate(u);
      var dx = position[0] - ox;
      var dy = position[1] - oy;
      var velocity = neweval.velocity(u);
      var denom = velocity[0] * velocity[0] + velocity[1] * velocity[1];
      var rate = 2 * (velocity[0] * dy - velocity[1] * dx) / denom;
      return [ox - velocity[1] * rate, oy + velocity[0] * rate, 0];
    },
    "endPoints":function() {
      return neweval.endPoints();
    }
  };
}
/* exported curveCatacaustic */
function curveCatacaustic(evaluator, ox, oy) {
  "use strict";
  return curveEvolute(curveOrthotomic(evaluator, ox, oy));
}
/* exported curvePedalCurve */
function curvePedalCurve(evaluator, ox, oy) {
  "use strict";
  var neweval = evaluator;
  return {
    "evaluate":function(u) {
      var position = neweval.evaluate(u);
      var velocity = neweval.velocity(u);
      var velocityXSq = velocity[0] * velocity[0];
      var velocityYSq = velocity[1] * velocity[1];
      var tanXY = velocity[0] * velocity[1];
      var denom = velocityXSq + velocityYSq;
      return [
        (ox * velocityXSq + velocityYSq * position[0] + (oy - position[1]) * tanXY) / denom,
        (oy * velocityYSq + velocityXSq * position[1] + (ox - position[0]) * tanXY) / denom,
        0];
    },
    "endPoints":function() {
      return neweval.endPoints();
    }
  };
}

function curveInverse(evaluator, ox, oy, radius) {
  "use strict";
  var neweval = evaluator;
  return {
    "evaluate":function(u) {
      var position = neweval.evaluate(u);
      var dx = position[0] - ox;
      var dy = position[1] - oy;
      var rsq = radius * radius;
      var denom = dx * dx + dy * dy;
      return [ox + dx * rsq / denom, oy + dy * rsq / denom];
    },
    "endPoints":function() {
      return neweval.endPoints();
    }
  };
}

/* exported ruledSurface */
function ruledSurface(directrix, director) {
  "use strict";
  return new H3DU.Curve({
    "evaluate":function(u, v) {
      var dx = directrix.evaluate(u);
      var dr = _vecScale(director.evaluate(u), v);
      return _vecAdd(dx, dr);
    },
    "endPoints":function() {
      var ep = directrix.endPoints();
      return [ep[0], ep[1], 0, 1];
    }
  });
}

var simpleCurve = new H3DU.Curve({
  "evaluate":function(u) {
    "use strict";
    return [Math.cos(u) * 1.5, Math.sin(u) * 0.8, 0];
  },
  "endPoints":function() {
    "use strict"; return [-Math.PI, Math.PI];
  }
});

var scene = new H3DU.Scene3D(document.getElementById("canvas"));
scene.setClearColor("white");
var sub = new H3DU.Batch3D()
   .perspectiveAspect(45, 1, 1000)
   .setLookAt([0, 0, 10]);
var group = new H3DU.ShapeGroup();
sub.getLights().setBasic();
var mesh = makeCurvePath(simpleCurve);
group.addShape(
    new H3DU.Shape(mesh).setMaterial(H3DU.Material.fromBasic("blue")));
mesh = makeCurvePath(curveInvolute(simpleCurve));
group.addShape(
    new H3DU.Shape(mesh).setMaterial(H3DU.Material.fromBasic("red")));
mesh = makeCurvePath(curveEvolute(simpleCurve));
group.addShape(
    new H3DU.Shape(mesh).setMaterial(H3DU.Material.fromBasic("orange")));
mesh = makeCurvePath(curveInverse(simpleCurve, 0, 0, 1));
group.addShape(
    new H3DU.Shape(mesh).setMaterial(H3DU.Material.fromBasic("green")));
sub.addShape(group);
H3DU.renderLoop(function() {
  "use strict";
  scene.render(sub);
});
// -->
</script>
</body>
